#include "./webpage.h"
#include "./winview.h"
#include "io/winstub.h"

using namespace MiniUI;
using namespace MiniUI::Type;

const char* TypeObject<WebPage>::s_szTypeName = "WebPage";

WebPage::WebPage()
: UIObject(GetTypeName())
{
    m_pWebhost = new WebHost(this);
    m_pWebhost->OnInvalidate = EventObject<OnInvalidateFunc>(this, &WebPage::OnWebInvalidate);
}

WebPage::~WebPage()
{
    delete m_pWebhost;
    m_pWebhost = nullptr;

    m_pWinView->OnWindowActivate -= EventObject<OnWindowActivateEventFunc>(this, &WebPage::OnWindowActivate);
}

HRESULT WebPage::SendMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    return m_pWebhost->SendMessage(uMsg, wParam, lParam);
}

bool WebPage::RenderTo(Graphics& graphics, const MiniRect& rcRender, const uint8 blendValue)
{
    if (!m_bVisible) return false;
    MiniRect rcWebRender = rcRender;
    rcWebRender.OffsetRect(-m_pos);

    Bitmap bitmap;
    MiniSize bitsBound;

    byte* pBits = m_pWebhost->DoPaint(rcWebRender, bitsBound); 
    bitmap.Rebuild(bitsBound, pBits);
    
    graphics.Save();
    UIObject::ShapeClip(graphics);
    MiniRect rcSrc(rcWebRender.left, rcWebRender.top, 
        rcWebRender.right > bitsBound.cx ? bitsBound.cx : rcWebRender.right, 
        rcWebRender.bottom > bitsBound.cy ? bitsBound.cy : rcWebRender.bottom);
    MiniRect rcDst(rcRender.left, rcRender.top, 
        rcRender.right > rcRender.left + bitsBound.cx ? rcRender.left + bitsBound.cx : rcRender.right, 
        rcRender.bottom > rcRender.top + bitsBound.cy ? rcRender.top + bitsBound.cy : rcRender.bottom);
    graphics.DrawBitmap(bitmap, &rcSrc, rcDst, ((uint16)m_blendValue * (1 + blendValue)) >> 8);
    graphics.Restore();

    return true;
}

bool WebPage::LoadURL(const char* szURL)
{
    return m_pWebhost->LoadURL(szURL);
}

void WebPage::SetWinView(WinView* pWinView)
{
    if (m_pWinView)
    {
        m_pWinView->OnWindowActivate -= EventObject<OnWindowActivateEventFunc>(this, &WebPage::OnWindowActivate);
        m_pWinView = nullptr;
    }
    UIObject::SetWinView(pWinView);
    pWinView->OnWindowActivate += EventObject<OnWindowActivateEventFunc>(this, &WebPage::OnWindowActivate);
}

bool WebPage::DblClk(const MiniPoint& pt, MouseButton button, uint keyState, bool& bHandled)
{
    if (!m_pWinView) return false;

    bHandled = true;
    m_pWebhost->DoDblClk(pt, button, keyState);
    return true;
}

bool WebPage::MouseDown(const MiniPoint& pt, MouseButton button, uint keyState, bool& bHandled)
{
    if (!m_pWinView) return false;

    bHandled = true;
    SetFocus(true);
    return m_pWebhost->DoMouseDown(pt, button, keyState, bHandled);
}

bool WebPage::MouseUp(const MiniPoint& pt, MouseButton button, uint keyState, bool& bHandled)
{
    if (!m_pWinView) return false;

    bHandled = true;
    m_pWebhost->DoMouseUp(pt, button, keyState);
    return true;
}

bool WebPage::MouseMove(const MiniPoint& pt, uint keyState, bool& bHandled)
{
    if (!m_pWinView) return false;

    bHandled = true;
    m_pWinView->SetCursor(m_cursor, m_customCursor);
    m_pWebhost->DoMouseMove(pt, keyState);
    return true;
}

void WebPage::MouseLeave()
{
    if (!m_pWinView) return;

    m_pWebhost->DoMouseLeave();
}

bool WebPage::MouseWheel(const MiniPoint& pt, uint keyState, int16 zDelta, bool& bHandled)
{
    if (!m_pWinView) return false;

    bHandled = true;
    m_pWebhost->DoMouseWheel(pt, keyState, zDelta);
    return true;
}

void WebPage::VisibleChange(bool bVisible)
{
    if (!m_pWinView) return;
    m_pWebhost->DoVisible(bVisible);
}

void WebPage::FocusChange(bool bFocus)
{
    if (!m_pWinView) return;
    m_pWebhost->DoFocus(bFocus);
}

void WebPage::Resize(const MiniSize& szOld, const MiniSize& szNew)
{
    m_pWebhost->DoSize(szNew);
}

void WebPage::OnWindowActivate(const WinView* sender, bool bActivate)
{
    m_pWebhost->DoWindowActivate(bActivate);
}

void WebPage::OnWebInvalidate(WebHost* pWebHost, MiniRect* rcDirty)
{
    if (rcDirty)
    {
        DoChange(*rcDirty);
    }
    else
    {
        DoChange(MiniRect(m_pos, m_pos + m_size));
    }
}